﻿#include "CentralCache.h"
#include "PageCache.h"

// 初始化CentralCache
CentralCache CentralCache::_sInst;

// 将_freeList中的小块内存归还给span
void CentralCache::ReleaseListToSpan(void* start, size_t size)
{
	// 先找到size对应的桶下标
	size_t index = SizeClass::GetBucketIndex(size);
	// 要对CentralCache中的桶进行操作，所以需要加锁
	_spanLists[index].GetMutex().lock();
	while (start)
	{
		void* next = NextObj(start);
		// span每次都更新了吗？频繁调用效率下降
		// Span* span = PageCache::GetInstance()->MapObjectToSpan(start);
		Span* span = PageCache::GetInstance()->MapObjectToSpan(start);
		// 头插过程
		NextObj(start) = span->_freeList;
		span->_freeList = start;
		span->_useCount--;

		// 说明span切分出的所有小块内存都回来了
		if (span->_useCount == 0)
		{
			_spanLists[index].Erase(span);
			span->_freeList = nullptr;
			span->_next = nullptr;
			span->_prev = nullptr;

			// 释放span还给page，使用PageCache的锁即可
			// 解掉桶锁
			_spanLists[index].GetMutex().unlock();

			PageCache::GetInstance()->GetMutex().lock();
			PageCache::GetInstance()->ReleaseSpanToPageCache(span);
			PageCache::GetInstance()->GetMutex().unlock();

			_spanLists[index].GetMutex().lock();
		}

		start = next;
	}

	_spanLists[index].GetMutex().unlock();
}

// 获得一个非空的Span
Span* CentralCache::GetOneSpan(SpanList& list, size_t size) // 获得span
{
	// 查看当前的spanlist中是否还有未分配的span
	Span* it = list.Begin();
	// 如果还有未分配的span
	while (it != list.End())
	{
		if (it->_freeList != nullptr)
		{
			return it;
		}
		else
		{
			 it = it->_next;
		}
	}

	// 这里先解掉桶锁
	list.GetMutex().unlock();

	// 没有未分配的span了，只能找PageCache要
	PageCache::GetInstance()->GetMutex().lock();
	Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));

	span->_isUse = true;
	span->_objSize = size;
	PageCache::GetInstance()->GetMutex().unlock();

	list.GetMutex().lock();
	// 将要的span进行分割
	// 得到页的起始地址
	char* start = (char*)(span->_pageId << PAGE_SHIFT);
	// 得到大块内存的字节数
	size_t bytes = span->_pageNum << PAGE_SHIFT;
	char* end = start + bytes;

	// 把大块内存切成小块内存，用自由链表连接起来
	// 因为_freeList为空，给它一个初始值
	span->_freeList = start;
	if (start == nullptr)
	{
		std::cout << "GetSpan error!" << std::endl;
	}
	// 通过size来控制每一个小块内存的范围
	start += size;

	// 注意这里的tail
	void* tail = span->_freeList;
	// size_t k = 0; 统计尾插次数
	while (start < end)
	{
		// tail的下一个为start
		NextObj(tail) = start;
		// 将tail更新为新的尾
		tail = NextObj(tail);
		// 调整小块内存
		start += size;
		// ++k;
	}

	NextObj(tail) = nullptr;
	// 切好span之后，将span挂到桶里去，然后进行加锁
	// 头插span
	list.PushFront(span);
	//list.GetMutex().lock(); //锁要加到上面

	return span;
}

size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
{
	size_t index = SizeClass::GetBucketIndex(size);

	// 加桶锁
	_spanLists[index].GetMutex().lock();

	Span* span = GetOneSpan(_spanLists[index], size);
	assert(span);
	assert(span->_freeList);

	start = span->_freeList;
	end = start;
	size_t actualNum = 1;
	size_t i = 0;
	// 头删,如果不够有多少拿多少
	while (actualNum < batchNum && NextObj(end) != nullptr)
	{
		end = NextObj(end);
		++actualNum;
	}

	span->_freeList = NextObj(end);
	NextObj(end) = nullptr;
	span->_useCount += actualNum;

	_spanLists[index].GetMutex().unlock();

	return actualNum;
}